package ws

import (
	"context"
	"encoding/json"
	"io"
	"log"
	"strconv"
	"strings"
	"sync"
	"time"

	"device-admin/config/redis"
	"device-admin/internal/app/grpc/service/robot"
	"device-admin/internal/define"

	gredis "github.com/go-redis/redis/v8"

	"192.168.1.75/go-pkg/logx"
	"github.com/gorilla/websocket"
)

// MsgSt 消息返回体内容
type MsgRTSt struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// RobotRealtimeInfoHandle 机器人实时信息
func RobotRealtimeInfoHandle(wsConn *websocket.Conn, msgType string, authorization string) {
	var pubsub *gredis.PubSub
	var robotIDs []string
	var wlock sync.RWMutex

	// 结束连接
	connCtx, connCancel := context.WithCancel(context.Background())
	defer connCancel()

	// 延迟
	timeoutTicker := time.NewTicker(time.Minute * 10)
	defer timeoutTicker.Stop()

	// 要订阅的topic
	topicPrefix := ""
	subFlagPrefix := ""
	switch msgType {
	case "robot_realtime_info":
		topicPrefix = define.RedisRobotRTInfo
		subFlagPrefix = define.RedisRobotRTInfoSubFlag
	case "robot_realtime_alarm_info":
		topicPrefix = define.RedisRobotRTAlarmInfo
		subFlagPrefix = define.RedisRobotRTAlarmInfoSubFlag
	}

	// 连接断开处理
	wsConn.SetCloseHandler(func(code int, text string) error {
		connCancel()
		return nil
	})

	// 延迟关闭订阅
	defer func() {
		logx.Info(context.Background(), "websocket disconnect", logx.String("robot_id", strings.Join(robotIDs, ",")))
		if pubsub != nil {
			pubsub.Close()
		}
	}()

	// 异常恢复
	defer func() {
		if err := recover(); err != nil {
			log.Println("webSocket err", err)
			// 结束连接
			connCancel()
		}
	}()

	// 接收信息
	go func(connCtx context.Context) {
		defer func() {
			if err := recover(); err != nil {
				// 结束连接
				connCancel()
			}
		}()
		for {
			select {
			case <-timeoutTicker.C:
				connCancel()
			case <-connCtx.Done():
				// 取消订阅
				for _, robotID := range robotIDs {
					timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*3)
					if pubsub != nil {
						pubsub.Unsubscribe(timeoutCtx, topicPrefix+robotID)
					}
					cancel()
				}
				return
			default:
				_, message, err := wsConn.ReadMessage()
				wlock.Lock() // 避免设备切换时，不同设备的数据混乱
				// 连接检查
				if err == io.EOF {
					// 结束连接
					connCancel()
				}
				// 消息处理
				if err == nil && len(message) > 0 {
					// 接收参数
					var params struct {
						RobotIDs string `json:"robot_ids"`
					}
					json.Unmarshal(message, &params)
					if params.RobotIDs != "" {
						if pubsub != nil {
							pubsub.Close() // 不关闭会内存泄露
							pubsub = nil
						}
						robotIDs = strings.Split(params.RobotIDs, ",")
						// 消息重新订阅
						for _, robotID := range robotIDs {
							// 切换设备时，重新用户观看摄像头标记
							viewerID := define.RedisDeviceCameraViewer + robotID + ":" + authorization
							redis.NewDB().SetEX(context.Background(), viewerID+":1", 1, time.Second*1)
							redis.NewDB().SetEX(context.Background(), viewerID+":2", 1, time.Second*1)
							redis.NewDB().SetEX(context.Background(), viewerID+":3", 1, time.Second*1)
							redis.NewDB().SetEX(context.Background(), viewerID+":4", 1, time.Second*1)
							redis.NewDB().SetEX(context.Background(), viewerID+":5", 1, time.Second*1)
							// 切换设备时，重新订阅设备实时数据
							func() {
								redisCtx, redisCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
								defer redisCtxCancel()
								if pubsub == nil {
									pubsub = redis.NewDB().Subscribe(redisCtx, topicPrefix+robotID)
								} else {
									pubsub.Subscribe(redisCtx, topicPrefix+robotID)
								}
								// 设置订阅标记
								redis.NewDB().Set(redisCtx, subFlagPrefix+robotID, 1, time.Second*10)
								// 启动云点推送
								go robot.WildmsgNotifyPrimitivePointCloud(robotID, true)
							}()
						}
					}
				}
				wlock.Unlock()
				time.Sleep(time.Millisecond * 100)
			}
		}
	}(connCtx)

	// 长连接消息处理
	for {
		// 订阅续期-1s
		func() {
			redisCtx, redisCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
			defer redisCtxCancel()
			for _, robotID := range robotIDs {
				func() {
					redis.NewDB().Expire(redisCtx, subFlagPrefix+robotID, time.Second*10)
				}()
			}
		}()

		// redis缓存设置推流标志-延期10s
		func() {
			// 推流保活
			if len(robotIDs) == 1 {
				pushFlowKeepalive(robotIDs[0], authorization)
			}
		}()

		// pubsub 空判断
		{
			if pubsub == nil {
				select {
				// 连接超时
				case <-connCtx.Done():
					return
				default:
					time.Sleep(time.Millisecond * 100)
				}
				continue
			}
		}

		// 发送消息
		{
			wlock.Lock()
			select {
			// 连接超时
			case <-connCtx.Done():
				wlock.Unlock()
				return
			// 已收到设备信息
			case msg := <-pubsub.Channel():
				robotRTInfo := msg.Payload
				// 发送消息
				ret := MsgRTSt{}
				ret.Code = 0
				ret.Msg = ""
				ret.Data = robotRTInfo
				tetBytes, _ := json.Marshal(ret)
				// 注意，并发写入会异常
				wsConn.WriteMessage(1, tetBytes)
			default:
				time.Sleep(time.Millisecond * 100)
			}
			wlock.Unlock()
		}
	}
}

// pushFlowKeepalive  推流保活
func pushFlowKeepalive(robotID, authorization string) {
	redisCtx, redisCtxCancel := context.WithTimeout(context.Background(), time.Second*3)
	defer redisCtxCancel()
	// 回充摄像头
	{
		cameraType := 1
		viewerID := define.RedisDeviceCameraViewer + robotID + ":" + authorization
		count1, _ := redis.NewDB().Exists(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType)).Result()
		count2, _ := redis.NewDB().Exists(redisCtx, viewerID+":"+strconv.Itoa(cameraType)).Result()
		if count2 > 0 {
			redis.NewDB().Expire(redisCtx, viewerID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
		if count1 > 0 && count2 > 0 {
			redis.NewDB().Expire(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
	}
	// RGBD摄像头
	{
		cameraType := 2
		viewerID := define.RedisDeviceCameraViewer + robotID + ":" + authorization
		count1, _ := redis.NewDB().Exists(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType)).Result()
		count2, _ := redis.NewDB().Exists(redisCtx, viewerID+":"+strconv.Itoa(cameraType)).Result()
		if count2 > 0 {
			redis.NewDB().Expire(redisCtx, viewerID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
		if count1 > 0 && count2 > 0 {
			redis.NewDB().Expire(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
	}
	// 前置摄像头
	{
		cameraType := 3
		viewerID := define.RedisDeviceCameraViewer + robotID + ":" + authorization
		count1, _ := redis.NewDB().Exists(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType)).Result()
		count2, _ := redis.NewDB().Exists(redisCtx, viewerID+":"+strconv.Itoa(cameraType)).Result()
		if count2 > 0 {
			redis.NewDB().Expire(redisCtx, viewerID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
		if count1 > 0 && count2 > 0 {
			redis.NewDB().Expire(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
	}
	// 左侧摄像头
	{
		cameraType := 4
		viewerID := define.RedisDeviceCameraViewer + robotID + ":" + authorization
		count1, _ := redis.NewDB().Exists(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType)).Result()
		count2, _ := redis.NewDB().Exists(redisCtx, viewerID+":"+strconv.Itoa(cameraType)).Result()
		if count2 > 0 {
			redis.NewDB().Expire(redisCtx, viewerID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
		if count1 > 0 && count2 > 0 {
			redis.NewDB().Expire(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
	}
	// 右侧摄像头
	{
		cameraType := 5
		viewerID := define.RedisDeviceCameraViewer + robotID + ":" + authorization
		count1, _ := redis.NewDB().Exists(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType)).Result()
		count2, _ := redis.NewDB().Exists(redisCtx, viewerID+":"+strconv.Itoa(cameraType)).Result()
		if count2 > 0 {
			redis.NewDB().Expire(redisCtx, viewerID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
		if count1 > 0 && count2 > 0 {
			redis.NewDB().Expire(redisCtx, define.RedisISUnderMonitoring+robotID+":"+strconv.Itoa(cameraType), time.Second*1)
		}
	}
}
